#include "../PreCompile.hpp"
#include "../CLangModule.hpp"

#include "VCCompilerBackend.hpp"
#include "Package.hpp"

namespace BuildNG
{
  bool VCCompilerBackend::is_available()
  {
    Glib::ustring cl_path = Settings::get_setting_string(
      "/buildng-settings/vc-compiler-settings/cl-path");
    if(!Glib::file_test(cl_path,Glib::FILE_TEST_EXISTS))
      return false;

    Glib::ustring linker_path = Settings::get_setting_string(
      "/buildng-settings/vc-compiler-settings/link-path");    
    if(Glib::file_test(linker_path,Glib::FILE_TEST_EXISTS))
      return false;

    Glib::ustring lib_path = Settings::get_setting_string(
      "/buildng-settings/vc-compiler-settings/lib-path");    
    if(Glib::file_test(lib_path,Glib::FILE_TEST_EXISTS))
      return false;

    return true;
  }

  Glib::RefPtr<VCCompilerBackend> VCCompilerBackend::create()
  {
    return Glib::RefPtr<VCCompilerBackend>(new VCCompilerBackend);
  }

  bool VCCompilerBackend::build_source_file(
    BuildContext&                 context,
    const Glib::ustring&          source_file_name,
    Glib::ustring&                obj_file_name,
    Glib::RefPtr<ErrorListener>   error_listener)
  {
    Glib::ustring cl_path = Settings::get_setting_string(
      "/buildng-settings/vc-compiler-settings/cl-path");
    
    EGlib::Process proc(cl_path);
    proc.set_working_directory(context.get_cur_dir());

    Glib::ustring default_flags;
    if(Settings::try_get_setting_string("/buildng-settings/vc-compiler-settings/default-flags",default_flags))
    {
      proc.add_arguments_string(default_flags,EGlib::Process::ArgumentParseMethod_Windows);
    }

    if(language == Language_PlainC)
      proc.add_argument("/TC");
    else
    {
      assert(language == Language_CPlusPlus);
      proc.add_argument("/TP");
    }

    Glib::ustring path_env;
    std::vector<Glib::ustring> paths_from_settings = Settings::get_setting_strarr(
      "/buildng-settings/vc-compiler-settings/path-env");
    for(int i = 0;i < (int)paths_from_settings.size();i++)
    {
      path_env += paths_from_settings[i] + ";";
    }
    path_env += Glib::getenv("PATH");
    proc.add_env("PATH",path_env);

    std::vector<Glib::ustring> inc_from_settings = Settings::get_setting_strarr(
      "/buildng-settings/vc-compiler-settings/include-env");
    Glib::ustring inc_env;
    for(int i = 0;i < (int)inc_from_settings.size();i++)
    {
      inc_env += inc_from_settings[i] + ";";
    }
    inc_env += Glib::getenv("INCLUDE");
    proc.add_env("INCLUDE",inc_env);

    // add the cmd line arguments   
    proc.add_argument(source_file_name);
    proc.add_argument("/c");    // only compile

    obj_file_name = source_file_name + ".obj";
    proc.add_argument("/Fo" + obj_file_name);

    Glib::ustring cflags_pkg_args = get_cflags_pkg_args();
    if(!cflags_pkg_args.empty())
      proc.add_arguments_string(cflags_pkg_args,EGlib::Process::ArgumentParseMethod_Windows);

    int cl_res = proc.spawn_sync();
    
    return cl_res == 0;
  }

  bool VCCompilerBackend::link(
    BuildContext&                 context,
    const std::vector<Glib::ustring> &object_files,
    Glib::RefPtr<ErrorListener>   error_listener)
  {
    Glib::ustring linker_path = Settings::get_setting_string(
      "/buildng-settings/vc-compiler-settings/link-path");
    
    EGlib::Process proc(linker_path);
    proc.set_working_directory(context.get_cur_dir());

    proc.add_argument("/nologo");

    Glib::ustring path_env;
    std::vector<Glib::ustring> paths_from_settings = Settings::get_setting_strarr(
      "/buildng-settings/vc-compiler-settings/path-env");
    for(int i = 0;i < (int)paths_from_settings.size();i++)
    {
      path_env += paths_from_settings[i] + ";";
    }
    path_env += Glib::getenv("PATH");
    proc.add_env("PATH",path_env);

    std::vector<Glib::ustring> libpaths_from_settings = Settings::get_setting_strarr(
      "/buildng-settings/vc-compiler-settings/lib-env");
    Glib::ustring lib_env;
    for(int i = 0;i < (int)libpaths_from_settings.size();i++)
    {
      lib_env += libpaths_from_settings[i] + ";";
    }
    lib_env += Glib::getenv("LIB");
    proc.add_env("LIB",lib_env);

    for(int i = 0;i < (int)object_files.size();i++)
    {
      proc.add_argument(object_files[i]);
    }

    Glib::ustring linker_pkg_args = get_linker_pkg_args();
    if(!linker_pkg_args.empty())
      proc.add_arguments_string(linker_pkg_args,EGlib::Process::ArgumentParseMethod_Windows);

    int linker_res = proc.spawn_sync();

    return linker_res == 0;
  }

  // settings related functions
  void VCCompilerBackend::add_reference(Glib::RefPtr<CLangReference> reference)
  {
    switch(reference->reference_type)
    {
    case CLangReference::ReferenceType_Package:
      {
        Glib::RefPtr<VCPackage> package = VCPackage::get_package(reference->name);
        assert(package);
        packages.insert(package);

        package->get_dependencies_recursive(packages);
      }
      break;

    case CLangReference::ReferenceType_Module:
      assert(!"not implemented yet");
      break;

    default:
      assert(!"unknown reference type");
      break;
    }
  }

  Glib::ustring VCCompilerBackend::get_cflags_pkg_args() const
  {
    Glib::ustring ret;
    for(VCPackageSet::const_iterator it = packages.begin();it != packages.end();++it)
    {
      ret += (*it)->get_cflags() + " ";
    }
    return ret;
  }

  Glib::ustring VCCompilerBackend::get_linker_pkg_args() const
  {
    Glib::ustring ret;
    for(VCPackageSet::const_iterator it = packages.begin();it != packages.end();++it)
    {
      ret += (*it)->get_linker_flags() + " ";
    }
    trim(ret);
    return ret;
  }
}
